home *** CD-ROM | disk | FTP | other *** search
- * This here is startup code for Amiga Aztec C programs, to make them pure.
- * Assemble it with the command "as purify.a" and link the resulting purify.o
- * file with your C program, in some manner like "ln yourprogram.o purify.o -lc",
- * and your program can then be marked as pure, and made resident, IF it meets
- * certain conditions:
- *
- * 1) You must not use the large data model for accessing your global variables.
- *
- * 2) You MUST NOT call the geta4() function from within any part of your program
- * that is run outside of your main process. Subtasks, interrupt handlers, and
- * wedges installed with SetFunction may not use geta4, and thus may not access
- * your global variables by any direct method. Note that #pragma intfunc causes
- * an implicit call of geta4().
- *
- * 3) You must not initialize global variables using the addresses of other
- * global variables. For example, the following is not correct:
- *
- * char foo[100], bar[100];
- * char *foobar[2] = { foo, bar }; /* WRONG */
- *
- * If you do this, then any references to foobar[0] will end up accessing
- * DIFFERENT MEMORY than accessing foo directly will. Instead, you have to do
- * such initialization in the code, with statements like "foobar[0] = foo;
- * foobar[1] = bar;".
- *
- * 4) You must not use detach.o -- this applies to ALL pure programs, not just
- * those that use this startup code.
- *
- * 5) Do not use this when making a shared library or device driver. ("Duh...")
- *
- * This was originally based on resident.asm by Olaf Barthel, which is available
- * on fish disk 396, but does not contain any of his code. Barthel's version had
- * serious errors in it, and will not work in many cases. It was also a lot less
- * efficient than the method I use.
- *
- * With the Aztec assembler, do not use the -N flag (one pass), because there's
- * a bug that makes the ENTRY directive not work with that option. It will need
- * a few changes in syntax to work with other assemblers. It might be possible
- * to make versions for other C compilers, so I've labeled everything which I
- * know to be Aztec specific with a comment containing the word "Aztec".
- *
- * The one possibly not quite cool bit of programming used here is that I used
- * SysBase->ThisTask instead of FindTask(0), in several places. If they ever
- * make an Amiga that runs tasks on several CPUs at once, that might break.
- *
- * This is by Paul Kienitz, 1 December 1991, placed in the public domain.
- * Revised 11 March 1992 to remove mistake -- DOSBase was accessed after the
- * memory it was in was already freed. A couple other little changes. Some
- * changes to this comment, but not to the code, were made later.
-
-
- ; We avoid pulling in include files by copying a few definitions here:
-
- MEMB_FAST equ 2 ; exec/memory.i
- MEMB_CLEAR equ 16 ; exec/memory.i
- ThisTask equ 276 ; exec/execbase.i
- AttnFlags equ 296 ; exec/execbase.i
- AFB_68881 equ 4 ; exec/execbase.i
- AG_OpenLib equ $30000 ; exec/alerts.i
- AO_DOSLib equ $8007 ; exec/alerts.i
- pr_ReturnAddr equ 176 ; libraries/dosextens.i
- pr_CLI equ 172 ; libraries/dosextens.i
- pr_MsgPort equ 92 ; libraries/dosextens.i
-
- _LVOTypeOfMem equ -534
- _LVOAllocMem equ -198
- _LVOOldOpenLibrary equ -408
- _LVOAlert equ -108
- _LVOSupervisor equ -30
- _LVOFreeMem equ -210
- _LVOCloseLibrary equ -414
- _LVOWaitPort equ -384
- _LVOGetMsg equ -372
- _LVOReplyMsg equ -378
- _LVOForbid equ -132
-
-
- lcall macro
- jsr _LVO\1(a6)
- endm
-
- AbsExecBase equ (4).w
-
- xdef .begin,_geta4
- xref __main,_SysBase,_DOSBase
- xref __savsp ; Aztec
- ;; xdef entree,cleanup ; for debugging
-
- ; the use of the label ".begin" for the entry point is an Aztec-ism
- entry .begin
-
- .begin:
- entree:
- far data ; Aztec
- lea __H1_org+32766,a4 ; to access original globals
-
- lea __H1_end,a1 ; Aztec; end of initialized data
- lea __H2_org,a2 ; Aztec; start of BSS
- near data ; Aztec
-
- cmp.l a1,a2 ; the same address?
- bne bomb ; if not, refuse to run
-
- move.l a0,d6 ; save command arg-line pointer
- move.l d0,d5 ; save command arg length
-
- lea __H1_org,a1 ; Aztec; start of data hunk
- move.l AbsExecBase,a6
- lcall TypeOfMem ; what kind of ram is it in?
- bclr.l #MEMB_FAST,d0 ; do NOT force fast ram!
- bset.l #MEMB_CLEAR,d0 ; do force cleared memory
-
- move.l d0,d1 ; memory type requirements
- move.l #(__H2_end-__H1_org),d0 ; Aztec; size of all globals
- lcall AllocMem ; space for a complete copy
- tst.l d0
- beq bomb ; if it failed, don't run
- move.l d0,a2
-
- lea __H1_org,a0 ; Aztec; address of old globals
- move.l a2,a1 ; address of new globals
- move.l #((__H1_end-__H1_org)/4)-1,d0 ; Aztec; size to copy
- copyit: move.l (a0)+,(a1)+ ; copy all initialized data
- dbra d0,copyit
-
- * Here's where we get tricky. We need to put the address of our dublicated
- * global variable space someplace where it can be found by geta4(), and we need
- * to make sure that we free the copied globals no matter how the program exits.
- * What we do is save the pointer on the stack, and then make it appear that the
- * final exit address that the program must return to is the address of our
- * cleanup routine. To do this, after pushing the pointer to the copied globals,
- * we push the size of the remaining available stack (12 bytes less than the
- * original size) and then the address of the cleanup code. We set the variable
- * __savsp to point to that cleanup code address, so that the Aztec exit()
- * function will return to that point. We set the pr_ReturnAddr field of our
- * struct Process to point one longword above that, to make the dos.library
- * Exit() function return to the cleanup code. This is also used by geta4() to
- * locate the saved pointer to the copied globals.
-
- move.l a2,-(sp) ; where geta4() can find it
- move.l 8(sp),a0 ; original size of the stack
- sub.w #12,a0 ; minus space we're using
- move.l a0,-(sp) ; make stack look smaller
- move.l ThisTask(a6),a0 ; equivalent to FindTask(0)
- move.l sp,pr_ReturnAddr(a0) ; for geta4() and dos Exit()
- pea cleanup ; so everybody goes there
-
- * The stack now looks like this:
- *
- * Stack size in bytes <- old pr_ReturnAddr pointed here
- * Return address for final exit <- __savsp would have pointed here
- * Pointer to globals for geta4 <- also used by cleanup
- * New stack size, minus twelve <- new pr_ReturnAddr points here
- * Fake return address: cleanup <- __savsp will point here
- *
- * Once __main is called, there will also be these:
- *
- * Command line arg address <- argument to __main
- * Command line arg length <- argument to __main ...SP is now here
- * Return address to this module <- falls through to cleanup
-
- bsr _geta4 ; use new globals
- move.l sp,__savsp ; Aztec; exit() and stack check
- move.l a6,_SysBase ; set up base for C program
- lea dosname,a1
- lcall OldOpenLibrary ; open dos.library
- move.l d0,_DOSBase ; set up another base
- bne dosokay
- move.l #AG_OpenLib!AO_DOSLib,d7 ; recoverable - no dos
- lea ThisTask(a6),a5 ; make it show our task adr
- lcall Alert ; frighten the user
- moveq #127,d0 ; return value
- rts ; "returns" to cleanup
-
- dosokay: btst.b #AFB_68881,AttnFlags+1(a6) ; is there an FPU?
- beq nofpu
- lea resetfpu,a5
- lcall Supervisor ; reset it in supervisor mode
-
- nofpu: movem.l d5/d6,-(sp) ; pass arguments to _main()
- jsr __main ; RUN THE PROGRAM!
- add.w #12,sp ; simulate rts to cleanup
-
- cleanup: move.l d0,d7 ; set aside the return value
- bsr _geta4 ; so we can check DOSBase
- move.l AbsExecBase,a6
- move.l _DOSBase,a1
- move.l a1,d0 ; fake tst.l a1
- beq dosneveropen ; was there ever a DOSBase?
- lcall CloseLibrary ; if yes, close it
-
- dosneveropen: move.l 4(sp),a1 ; address of copied globals
- move.l #(__H2_end-__H1_org),d0 ; size of copied globals
- lcall FreeMem ; get rid of the copy
- addq #8,sp ; restore primordial stack
- move.l ThisTask(a6),a1
- lea 4(sp),a0 ; just to be paranoid...
- move.l a0,pr_ReturnAddr(a1) ; restore old pr_ReturnAddr
-
- move.l d7,d0 ; restore the return value
- rts ; goodbye cruel world
-
-
- bomb: move.l AbsExecBase,a6 ; come here if we can't run
- move.l ThisTask(a6),a0
- tst.l pr_CLI(a0) ; are we a Workbench process?
- bne noworkbench ; if yes, then we need to
- ; reply the startup message:
- lea pr_MsgPort(a0),a0
- move.l a0,a3 ; remember our msgport address
- lcall WaitPort ; here come de message
- move.l a3,a0
- lcall GetMsg ; got it
- move.l d0,a3 ; yes, remember it
- lcall Forbid ; standard shutdown procedure
- move.l a3,a1
- lcall ReplyMsg ; tell WB to unload us
-
- noworkbench: moveq #127,d0 ; largest convenient return code
- rts ; might return to cleanup
-
-
- mc68881 ; Aztec; activate FPU instruction opcodes
-
- resetfpu: clr.l -(sp) ; call this in supervisor mode
- frestore (sp)+ ; reset the FPU
- rte ; return to user mode
-
-
- * Here's our magic routine to set up a4 for accessing the copied globals.
- * CALL ONLY FROM WITHIN THE SAME PROCESS.
-
- _geta4: move.l AbsExecBase,a4
- move.l ThisTask(a4),a4
- move.l pr_ReturnAddr(a4),a4 ; ptr to FAKE stack size
- move.l 4(a4),a4 ; stored ptr to copied globals
- lea 32766(a4),a4 ; add the offset
- rts
-
- dosname: dc.b 'dos.library',0 ; for OldOpenLibrary
-
- dseg ; Aztec
- ; Assembler bug: If you declare __H1_org public within the code segment,
- ; then the assembler refers to it with small data even if you have told
- ; it to use large data. Hence the above dseg is necessary. This does not seem
- ; to happen with ordinary symbols, just the __Hx_xxx ones.
- public __H1_org,__H1_end,__H2_org,__H2_end ; Aztec
-